﻿using IP2Region.Net.Abstractions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Shyjus.BrowserDetection;
using SqlSugar;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using Zhp.Api.Attributes;
using Zhp.Cache;
using Zhp.Common.Appsetting;
using Zhp.Common.Exception;
using Zhp.Common.Helper;
using Zhp.Common.WebApp;
using Zhp.Entity.Monitor;
using ICacheService = Zhp.Cache.ICacheService;

namespace Zhp.Api.Filters
{
    public class ApiActionFilter : IAsyncActionFilter
    {
        private readonly ISearcher _ipSearcher;
        private readonly IBrowserDetector _browser;
        private readonly ICacheService _cache;
        private readonly ICurrentUser _currentUser;
        private readonly ISqlSugarClient _sugarClient;
        public ApiActionFilter(
            ISearcher searcher, 
            IBrowserDetector browser, 
            ICacheService cache, 
            ICurrentUser currentUser,
            ISqlSugarClient sugarClient) 
        {
            _ipSearcher = searcher;
            _browser = browser;
            _cache = cache;
            _currentUser = currentUser;
            _sugarClient = sugarClient;
        }

        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            var sw = new Stopwatch();
            sw.Start();

            // 防重复提交
            var (repeat, repeatKey) = RepeatSubmit(context);

            var resultContext = await next();
            sw.Stop();

            var options = AppSettingHelper.GetOptions<SysDataOptions>();
            if (options.IsAuditLog)
            {
                var action = (ControllerActionDescriptor)context.ActionDescriptor;
                if (action.MethodInfo.IsDefined(typeof(NotAuditLogAttribute), false))
                {
                    return;
                }

                var auditLog = await CreateSysAuditLog(context);

                auditLog.ExecutionDuration = sw.ElapsedMilliseconds;

                var result = resultContext.Result as ObjectResult;
                var body = result?.Value.ToJson();
                auditLog.ResponseData = body;

                await _sugarClient.Insertable(auditLog).ExecuteCommandAsync();
            }

            // 如果没有设置过期时间，则方法执行完自动放行
            if (repeat == RepeatSubmitEnum.AutoExpire)
            {
                _cache.Remove(repeatKey);
            }
        }

        /// <summary>
        /// 防重复提交
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        /// <exception cref="AlertException"></exception>
        private (RepeatSubmitEnum, string) RepeatSubmit(ActionExecutingContext context)
        {
            var repeat = context.ActionDescriptor.EndpointMetadata.OfType<RepeatSubmitAttribute>().FirstOrDefault();
            if (repeat != null)
            {
                var request = context.HttpContext.Request;
                // 获取用户标识，这里假设使用身份验证后的用户 ID，你可以根据实际情况修改
                // 生成包含用户标识的请求唯一标识
                var key = $"{_currentUser.AccessTokenId}:{request.Method}:{request.Path}";

                if (_cache.Get<bool>(key))
                {
                    // 重复提交，返回错误响应
                    throw new AlertException("重复提交，请稍后再试");
                }

                if (repeat.Expire.HasValue)
                {
                    // 记录请求，设置过期时间
                    _cache.Set(key, true, TimeSpan.FromSeconds(repeat.Expire.Value));
                    return (RepeatSubmitEnum.FixedExpire, key);
                }
                else
                {
                    // 在Action执行完自动过期
                    _cache.Set(key, true);
                    return (RepeatSubmitEnum.AutoExpire, key);
                }
            }
            return (RepeatSubmitEnum.None, null);
        }

        private async Task<AuditLog> CreateSysAuditLog(ActionExecutingContext context)
        {
            var httpContext = context.HttpContext;
            var remoteIp = httpContext.GetIP();
            var args = await httpContext.GetAllRequestParams();
            var route = context.ActionDescriptor.RouteValues;
            // 优化路径比较
            bool isLoginPath = httpContext.Request.Path.Value?.Equals("/api/login/login", StringComparison.OrdinalIgnoreCase) ?? false;
            string createByName = isLoginPath ? GetUserNameFromLoginArgs(args) : _currentUser.UserName;

            // 优化反射操作
            var methodInfo = ((ControllerActionDescriptor)context.ActionDescriptor).MethodInfo;
            var desc = methodInfo.GetCustomAttribute<DescriptionAttribute>();
            var auditLog = new AuditLog
            {
                Controller = httpContext.GetControllerName(),
                Action = httpContext.GetActionName(),
                Method = httpContext.Request.Method,
                Description = desc?.Description,
                RequestUrl = httpContext.Request.Path,
                RequestParameters = args.ToJson(),
                RequestIp = remoteIp,
                IpAddress = _ipSearcher.Search(remoteIp),
                OperatingSystem = _browser.Browser?.OS,
                DeviceType = _browser.Browser?.DeviceType,
                BrowserName = _browser.Browser?.Name,
                Version = _browser.Browser?.Version,
                CreateByName = createByName
            };

            return auditLog;
        }

        private static string GetUserNameFromLoginArgs(Dictionary<string, object> args)
        {
            if (args.TryGetValue("userCode", out var value) && value != null)
            {
                return value.ToString();
            }
            return null;
        }
    }
}
